package com.lin.cloud.disk.manage.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.api.R;
import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;
import com.lin.cloud.disk.common.constant.RedisConstant;
import com.lin.cloud.disk.common.entity.request.AccountRequestDTO;
import com.lin.cloud.disk.common.enums.EDeleteStatus;
import com.lin.cloud.disk.common.enums.EUploadStatus;
import com.lin.cloud.disk.common.exception.BusinessException;
import com.lin.cloud.disk.entity.dto.LogicDeleteStatusDTO;
import com.lin.cloud.disk.entity.pojo.File;
import com.lin.cloud.disk.entity.request.directory.MoveDirectoryRequest;
import com.lin.cloud.disk.entity.request.directory.RestoreDirectoryRequest;
import com.lin.cloud.disk.entity.request.file.*;
import com.lin.cloud.disk.entity.response.*;
import com.lin.cloud.disk.manage.IFileManageService;
import com.lin.cloud.disk.service.IFileService;
import com.sun.scenario.animation.AbstractMasterTimer;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @program: cloud-disk
 * @description: 文件管理服务实现类
 * @author: Lin
 * @create: 2021-01-15 15:07
 **/
@Service
@Slf4j
public class FileManageServiceImpl implements IFileManageService {

    @Resource
    private MinioClient minioClient;

    @Resource
    private IFileService fileService;

    /**
     * 上传文件最小大小 单位K; 10M作为一段区分
     */
    private Long uploadPartMaxSize = 1024L * 1024 * 10;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private final String imgBucketName = "img";


    /**
     * 获取预上传的链接
     */
    @Override
    public GetPreUploadUrlResponse getPreUploadUrl(GetPreUploadUrlRequest request){
        GetPreUploadUrlResponse response = new GetPreUploadUrlResponse();
        List<String> uploadUrlList = this.genUploadUrlList("uploadId", "temp", 0, 1,"1.jpg");
        response.setUploadUrl(uploadUrlList.get(0));
        return response;
    }

    /**
     * 获取上传地址列表
     * @param request
     * @return
     */
    @SneakyThrows
    @Override
    public GetMultipartUploadUrlResponse getMultipartUploadUrl(GetMultipartUploadUrlRequest request){

        GetMultipartUploadUrlResponse response = new GetMultipartUploadUrlResponse();
        response.setUploadPartMaxSize(uploadPartMaxSize);
        List<Integer> successChuckList = new ArrayList<>();
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        if (null != request.getDirectoryId()){
            getUserFileRequest.setDirectoryId(request.getDirectoryId().toString());
        }
        getUserFileRequest.setObjectName(request.getObjectName());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setMd5(request.getMd5());
        // 先通过MD5值获取数据库是否存在
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);
        File file = null;

        String uploadId = "";

        // 获取文件大小
        Long fileSize = request.getFileSize();
        // 向上取整 确定需要多少部分
        Integer partTotalSize = (int) Math.ceil(fileSize / uploadPartMaxSize) + 1;

        if (CollectionUtils.isNotEmpty(fileList)){
            // 存在 获取第一个文件
            file = fileList.get(0);
            uploadId = file.getUploadId();
            // 判断是否已经上传成功
            if (EUploadStatus.DONE_UPLOAD.getStatus().equals(file.getUploadStatus())){
                // 上传完成
                if (EDeleteStatus.DELETE.getStatus().equals(file.getIsDelete())){
                    // 上传完成 但是被删除了 则恢复就行了
                    file.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
                    file.setUpdateBy(request.getUserDTO().getUsername());
                    file.setUpdateTime(new Date());
                    fileService.updateById(file);
                }
                // 不需要重新上传
                response.setUploadId(uploadId);
                response.setUploadUrlList(Collections.EMPTY_LIST);
                return response;
            }else{
                // 未上传完成操作 寻找未上传完的部分
                // 从minio中查找 参数 存储对象 文件名 不需要往下递归
                Iterator<Result<Item>> fileObjectIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(file.getBucket()).prefix(file.getUploadId()).build()).iterator();
                // 最后一个文件的信息
                Item lastFilePartItem = null;
                // 计算总量
                int nowFilePartCount = 0;
                while (fileObjectIterator.hasNext()){
                    lastFilePartItem = fileObjectIterator.next().get();
                    successChuckList.add(nowFilePartCount);
                    nowFilePartCount++;
                }
                // 判断实际大小与已上传的大小差多少个
                int differenceCount = partTotalSize - nowFilePartCount;

                if (differenceCount >= 0){
                    // 如果等于0 看看最后一个是不是上传了没通知合并 如果是 通知合并
                    if (differenceCount == 0 && null != lastFilePartItem){
                        // 最后一个文件的大小
                        long lastFileSize = lastFilePartItem.size();
                        // 计算未上传最后一个文件的大小
                        long computeLastFileSize = request.getFileSize() - ( uploadPartMaxSize * (partTotalSize - 1) );
                        // 相等 通知合并
                        if (lastFileSize == computeLastFileSize){
                            // 合并文件
                            MergeFilePartRequest mergeFilePartRequest = new MergeFilePartRequest();
                            mergeFilePartRequest.setUploadId(uploadId);
                            mergeFilePartRequest.setUserDTO(request.getUserDTO());
                            this.mergeFilePart(mergeFilePartRequest);
                            // 不需要重新上传
                            response.setUploadUrlList(Collections.EMPTY_LIST);
                            return response;
                        }
                    }
                    if (null != lastFilePartItem) {
                        // 需要删除最后一个文件
                        minioClient.removeObject(RemoveObjectArgs.builder().bucket(request.getUserDTO().getUsername()).object(lastFilePartItem.objectName()).build());
                        successChuckList.remove(successChuckList.size() - 1);
                    }
                    // 开始上传的位置
                    int startPartNumber = (nowFilePartCount - 1) >=0 ? (nowFilePartCount - 1) : 0;
                    // 生成differenceCount个上传链接提供上传
                    List<String> uploadUrlList = this.genUploadUrlList(file.getUploadId(), file.getBucket(), startPartNumber, partTotalSize, file.getObjectName());
                    // 生成剩余N个文件上传链接
                    response.setUploadId(uploadId);
                    response.setUploadUrlList(uploadUrlList);
                    response.setSuccessChunkList(successChuckList);
                    response.setNeedUpload(true);
                    return response;
                }
                throw new BusinessException("文件大小计算错误 differenceCount = " + differenceCount);
            }
        }else{
            // 不存在 做插入操作
            file = new File();
            // 生成uploadId
            uploadId = IdWorker.get32UUID();
            // 生成主键Id
            Long id = IdWorker.getId();

            String objectName = request.getObjectName();

            file.setId(id);
            file.setBucket(request.getUserDTO().getUsername());
            file.setObjectName(objectName);
            file.setUploadId(uploadId);
            if (request.getDirectoryId() == null){
                file.setDirectoryId(-1L);
            }else {
                file.setDirectoryId(request.getDirectoryId());
            }
            file.setUserId(request.getUserDTO().getId());
            this.setFileNameAndSuffix(file, objectName);
            file.setFileSize(request.getFileSize());
            file.setUploadStatus(EUploadStatus.UN_DONE_UPLOAD.getStatus());
            file.setMd5(request.getMd5());
            file.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
            file.setCreateBy(request.getUserDTO().getUsername());
            file.setCreateTime(new Date());
            // 插入文件
            fileService.createFile(file);

            List<String> uploadUrlList = this.genUploadUrlList(file.getUploadId(), file.getBucket(), 0, partTotalSize,"1.jpg");
            // 生成剩余N个文件上传链接
            response.setUploadUrlList(uploadUrlList);
            response.setUploadId(uploadId);
            response.setNeedUpload(true);
            response.setSuccessChunkList(successChuckList);
            return response;
        }
    }

    /**
     * 获取上传地址
     * @param request
     * @return
     */
    @SneakyThrows
    @Override
    public GetUploadUrlResponse getUploadUrl(GetUploadUrlRequest request){

        GetUploadUrlResponse response = new GetUploadUrlResponse();
        String url = null;
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        getUserFileRequest.setDirectoryId(request.getDirectoryId().toString());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setMd5(request.getMd5());
        // 先通过MD5值获取数据库是否存在
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);
        File file = null;
        if (CollectionUtils.isNotEmpty(fileList)){
            // 存在 获取第一个文件
            file = fileList.get(0);
            // 判断是否已经上传成功
            if (EUploadStatus.DONE_UPLOAD.getStatus().equals(file.getUploadStatus())){
                // 上传完成
                if (EDeleteStatus.DELETE.getStatus().equals(file.getIsDelete())){
                    // 上传完成 但是被删除了 则恢复就行了
                    file.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
                    file.setUpdateBy(request.getUserDTO().getUsername());
                    file.setUpdateTime(new Date());
                    fileService.updateById(file);
                    response.setUploadId(file.getUploadId());
                    return response;
                }
            }else {
                // 没上传成功则删除
                fileService.removeFileById(file.getId());
            }
        }
            // 不存在 做插入操作
            file = new File();
            // 生成uploadId
            String uploadId = IdWorker.get32UUID();
            // 生成主键Id
            Long id = IdWorker.getId();

            String objectName = request.getObjectName();

            file.setId(id);
            file.setBucket(request.getUserDTO().getUsername());
            file.setObjectName(objectName);
            file.setUploadId(uploadId);
            file.setDirectoryId(request.getDirectoryId());
            file.setUserId(request.getUserDTO().getId());
            this.setFileNameAndSuffix(file, objectName);
            file.setFileSize(request.getFileSize());
            file.setUploadStatus(EUploadStatus.UN_DONE_UPLOAD.getStatus());
            file.setMd5(request.getMd5());
            file.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
            file.setCreateBy(request.getUserDTO().getUsername());
            file.setCreateTime(new Date());
            // 插入文件
            fileService.createFile(file);
        List<String> list = this.genUploadUrlList(uploadId, file.getBucket(), 0, 1,file.getObjectName());
        if (CollectionUtil.isNotEmpty(list)){
            url = list.get(0);
            response.setNeedUpload(true);
        }
        response.setUploadUrl(url);
        response.setUploadId(file.getUploadId());
        return response;
    }

    /**
     * 设置文件名以及文件后缀
     * @param file
     * @param objectName
     */
    private void setFileNameAndSuffix(File file, String objectName) {
        // 切割文件后缀 文件名字
        int lastPointIndex = objectName.lastIndexOf(".");
        String fileName = "";
        String fileSuffix = "";
        if (-1 != lastPointIndex){
            // 前缀文件路径名
            fileName = objectName.substring(0, lastPointIndex);
            // 获取最后一个 / 切割
            int lastSlashIndex  = fileName.lastIndexOf("/");
            if (-1 != lastSlashIndex){
                // 如果存在 则去除前缀路径
                fileName = fileName.substring(lastSlashIndex + 1);
            }
            // 转为小写
            fileSuffix = objectName.substring(lastPointIndex + 1).toLowerCase();
            file.setFileSuffix(fileSuffix);
        }
        file.setFileName(fileName);
        // 如果文件名为空 则代表文件是以 .开头的 直接使用后缀作为名字
        if (StringUtils.isBlank(fileName)){
            file.setFileName(fileSuffix);
        }
    }


    /**
     * 上传完成后 合并多个文件
     * @param request
     */
    @SneakyThrows
    @Override
    public void mergeFilePart(MergeFilePartRequest request) {
        // 从数据库中查询是否已经上传成功了
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        getUserFileRequest.setUploadId(request.getUploadId());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);

        File file = null;
        // 数据库查询存在
        if (CollectionUtils.isNotEmpty(fileList)){
            file = fileList.get(0);
            // 已经上传成功则返回
            if (EUploadStatus.DONE_UPLOAD.getStatus().equals(file.getUploadStatus())){
                return;
            }
            // 未上传成功 获取文件大小
            Long fileSize = file.getFileSize();
            // 获取minio中文件的总数量
            Long ossFileSize = 0L;
            List<String> ossFileNameList = new ArrayList<>();
            Item item = null;
            Iterator<Result<Item>> fileObjectIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(file.getBucket()).prefix(file.getUploadId()).build()).iterator();
            while (fileObjectIterator.hasNext()){
                item = fileObjectIterator.next().get();
                // 计算文件大小
                ossFileSize += item.size();
                // 添加OSS已有的文件名
                ossFileNameList.add(item.objectName());
            }
            // 判断文件大小是否已上传完毕
            if (ossFileSize.equals(fileSize)){
                // 大小相等 则合并文件
                List<ComposeSource> sourceObjectList = new ArrayList<ComposeSource>(ossFileNameList.size());
                ossFileNameList = ossFileNameList.stream().sorted().collect(Collectors.toList());
                ComposeSource composeSource = null;
                for (String ossFileName: ossFileNameList) {
                    composeSource = ComposeSource.builder().bucket(file.getBucket()).object(ossFileName).build();
                    sourceObjectList.add(composeSource);
                }
                // 合并文件
                Map<String,String> headerMap = new HashMap<>();
                ContentInfo contentInfo = ContentInfoUtil.findExtensionMatch(file.getObjectName());
                if (null != contentInfo){
                    headerMap.put("response-content-type",contentInfo.getMimeType());
                }
                minioClient.composeObject(ComposeObjectArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).sources(sourceObjectList).userMetadata(headerMap).build());
                // 生成预览与下载链接
                String previewUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).method(Method.GET).extraQueryParams(headerMap).build());
                String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).method(Method.GET).build());

                String cacheKey = RedisConstant.STRAIGHT_DOWNLOAD_URL + request.getUserDTO().getId() + ":" +  file.getId();
                stringRedisTemplate.opsForValue().set(cacheKey, url);
                // 合并成功后 将文件更新为上传成功
                file.setUrl(url);
                file.setPreviewUrl(previewUrl);
                file.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
                file.setUpdateTime(new Date());
                file.setUpdateBy(request.getUserDTO().getUsername());
                fileService.updateById(file);

                for (String ossFileName: ossFileNameList) {
                    // 删除临时文件
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(file.getBucket()).object(ossFileName).build());
                }
            }else{
                // 文件大小不一致 通知重新获取上传地址 上传未上传部分
                throw new BusinessException("文件大小不一致, 已上传大小:" + ossFileSize +"  实际文件大小:" + fileSize);
            }
        }else {
            throw new BusinessException("uploadId找不到对应的文件,请检查!");
        }

    }


    /**
     * 逻辑删除文件
     * @param request
     */
    @Override
    public void logicRemoveFile(RemoveFileRequest request){
        // 从数据库中查询是否存在
        if (CollectionUtil.isEmpty(request.getIdList())){
            throw new BusinessException("请选择文件");
        }
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        getUserFileRequest.setIdList(request.getIdList());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);
        if (CollectionUtil.isEmpty(fileList)){
            throw new BusinessException("选择的文件不存在");
        }

        List<Long> operatorIdList = fileList.stream().map(File::getId).collect(Collectors.toList());
        // 取交集
        operatorIdList.retainAll(request.getIdList());
            // 否则逻辑删除
            LogicDeleteStatusDTO logicDeleteStatusDTO = LogicDeleteStatusDTO.getDeleteStatusInstance(operatorIdList,request.getUserDTO().getUsername());
            fileService.logicDeleteOrRecover(logicDeleteStatusDTO);
    }

    /**
     * 逻辑恢复文件
     * @param request
     */
    @Override
    public void logicRestoreFile(RestoreDirectoryRequest request){
        // 从数据库中查询是否存在
        if (CollectionUtil.isEmpty(request.getIdList())){
            throw new BusinessException("请选择文件");
        }
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        getUserFileRequest.setIdList(request.getIdList());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);
        if (CollectionUtil.isEmpty(fileList)){
            throw new BusinessException("选择的文件不存在");
        }

        List<Long> operatorIdList = fileList.stream().map(File::getId).collect(Collectors.toList());
        // 取交集
        operatorIdList.retainAll(request.getIdList());
        // 否则逻辑删除
        LogicDeleteStatusDTO logicDeleteStatusDTO = LogicDeleteStatusDTO.getUnDeleteStatusInstance(operatorIdList,request.getUserDTO().getUsername());
        fileService.logicDeleteOrRecover(logicDeleteStatusDTO);
    }

    /**
     * 物理删除文件
     * @param request
     */
    @SneakyThrows
    @Override
    public void physicalRemoveFile(RemoveFileRequest request){
        if (CollectionUtil.isEmpty(request.getIdList())){
            throw new BusinessException("请选择文件");
        }
        // 从数据库中查询是否存在
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        getUserFileRequest.setIdList(request.getIdList());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
        getUserFileRequest.setIsDelete(EDeleteStatus.DELETE.getStatus());
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);
        if (CollectionUtil.isEmpty(fileList)){
            throw new BusinessException("选择的文件不存在");
        }
        if (CollectionUtils.isNotEmpty(fileList)){
            for (File file:
                 fileList) {
                // 否则 物理删除
                // 从数据库中删除
                Integer count = fileService.removeFileById(file.getId());
                if (0 != count) {
                    // 从minio删除
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).build());
                }
            }
        }
    }

    /**
     * 获取直链下载链接 7天限制
     * @param request
     * @return
     */
    @Override
    @SneakyThrows
    public GetFileUrlResponse getStraightDownloadUrl(GetStraightDownloadUrlRequest request){
        GetFileUrlResponse response = new GetFileUrlResponse();
        Integer urlType = request.getUrlType();
        if (!urlType.equals(0) || !urlType.equals(1)){
            // 既然不是下载链接 也不是分享链接 则抛异常
            throw new BusinessException("链接类型选择错误");
        }

        // 根据Url类型选择Key
        String cacheKey = null;
        if (urlType.equals(0)){
            cacheKey = RedisConstant.STRAIGHT_DOWNLOAD_URL + request.getUserDTO().getId() + ":" +  request.getFileId();
        }else{
                cacheKey = RedisConstant.STRAIGHT_DOWNLOAD_SHARE_URL + request.getUserDTO().getId() + ":" +  request.getFileId();
        }

        String downloadUrl = stringRedisTemplate.opsForValue().get(cacheKey);
        // Redis存在 则从Redis获取
        if (StringUtils.isNotBlank(downloadUrl)){
            response.setUrl(downloadUrl);
            return response;
        }
        // 从数据库中查询是否存在
        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
        getUserFileRequest.setId(request.getFileId());
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
        getUserFileRequest.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
        List<File> fileList  =  fileService.getFileList(getUserFileRequest);
        if (CollectionUtils.isNotEmpty(fileList)){
            File file = fileList.get(0);
            if (null == file){
                throw new BusinessException("文件不存在");
            }

            Map<String,String> reqParams = new HashMap<>();
//        reqParams.put("response-content-type", "application/json");
            ContentInfo contentInfo = ContentInfoUtil.findExtensionMatch(file.getObjectName());
            if (null != contentInfo){
                reqParams.put("response-content-type",contentInfo.getMimeType());
            }
            if (urlType.equals(0)){
                downloadUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).method(Method.GET).extraQueryParams(reqParams).build());
                stringRedisTemplate.opsForValue().set(cacheKey, downloadUrl);
            }else {
                // 从minio获取分享下载地址 默认7天
                int expireds = 7;
                if (null != request.getExpiredDay() && 0 < request.getExpiredDay() && request.getExpiredDay() < 7){
                    // expiredDay存在并且在7天之内
                    expireds = request.getExpiredDay();
                }
                downloadUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).method(Method.GET).extraQueryParams(reqParams).build());
                stringRedisTemplate.opsForValue().set(cacheKey, downloadUrl, expireds, TimeUnit.SECONDS);
            }
            response.setUrl(downloadUrl);
            return response;
        }
        return null;
    }



    /**
     * 生成上传链接列表
     * @param uploadId
     * @param partNumber
     * @param partSize
     * @return
     */
    @SneakyThrows
    private List<String> genUploadUrlList(String uploadId, String bucketName, Integer partNumber, Integer partSize,String objectName){
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!flag){
            // 如果bucket不存在 则创建
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            String policyJson = "{\n" +
                    "  \"Statement\": [\n" +
                    "        {\n" +
                    "            \"Action\": [\n" +
                    "                \"s3:GetBucketLocation\",\n" +
                    "                \"s3:ListBucket\"\n" +
                    "            ],\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Principal\": \"*\",\n" +
                    "            \"Resource\": \"arn:aws:s3:::" + bucketName + "\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"Action\": \"s3:GetObject\",\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Principal\": \"*\",\n" +
                    "            \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" +
                    "        }\n" +
                    "    ]" +
                    "}";
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
        }

        Map<String,String> reqParams = new HashMap<>();
//        reqParams.put("response-content-type", "application/json");
        ContentInfo contentInfo = ContentInfoUtil.findExtensionMatch(objectName);
        if (null != contentInfo){
            reqParams.put("response-content-type",contentInfo.getMimeType());
        }
        // 开始生成上传链接
        List<String> uploadUrlList = new ArrayList<>(partSize - partNumber);
        String uploadObjectName = "";
        for (int i = partNumber; i< partSize; i++){
            // 上传文件名 uploadId_i;
            uploadObjectName = uploadId + "_" + i;
            // 生成链接
            String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(uploadObjectName).method(Method.PUT).extraQueryParams(reqParams).build());
            uploadUrlList.add(url);
        }
        return uploadUrlList;
    }


    /**
     * 获取文件指定存储日期报表
     * @param request
     * @return
     */
    @Override
    public List<GetFileStoreDateTableResponse> getFileStoreDateTable(GetFileStoreDateTableRequest request){

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
        Integer specifiedDayWithin = request.getSpecifiedDayWithin();
        if (null == specifiedDayWithin || specifiedDayWithin < 1){
            specifiedDayWithin = 1;
        }

        GetUserFileRequest getUserFileRequest = new GetUserFileRequest();

        Calendar calendar = Calendar.getInstance();
        // 当前时间为结束时间
        calendar.add(Calendar.DATE, -specifiedDayWithin);
        // dayAgo天前为开始时间

        getUserFileRequest.setStartTime(simpleDateFormat.format(calendar.getTime()));

        // 获取文件
        getUserFileRequest.setUserId(request.getUserDTO().getId());
        getUserFileRequest.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
        getUserFileRequest.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
        List<File> fileList = fileService.getFileList(getUserFileRequest);
        if (CollectionUtil.isEmpty(fileList)){
            return Collections.EMPTY_LIST;
        }

        // 按照日期分类
        Map<String,List<File>> dateAndFileListMap = new HashMap<>();
        List<File> list = null;
        for (File file : fileList) {
            String date = simpleDateFormat.format(file.getCreateTime());
            if (date.contains(date)){
                list = dateAndFileListMap.get(date);
                if (CollectionUtil.isEmpty(list)){
                    list = new ArrayList<>();
                }
                list.add(file);
            }else{
                list = Arrays.asList(file);
            }
            dateAndFileListMap.put(date,list);
        }

        // 统计内容
        List<GetFileStoreDateTableResponse> responsesList = new ArrayList<>(dateAndFileListMap.entrySet().size());
        GetFileStoreDateTableResponse response = null;
        // 1m大小
        BigInteger mSize = new BigInteger("1048576");
        for (Map.Entry<String,List<File>> entry: dateAndFileListMap.entrySet()) {
            response = new GetFileStoreDateTableResponse();
            String date = entry.getKey();
            list = entry.getValue();

            BigInteger bigInteger = new BigInteger("0");
            for (int i = 0; i < list.size() ; i++){
                bigInteger = bigInteger.add(new BigInteger(list.get(i).getFileSize().toString()));
            }
             // 转化为1m大小
            String totalFileSize = bigInteger.divide(mSize).toString();

            Integer totalSize = list.size();

            response.setDate(date);
            response.setTotalFileSize(totalFileSize);
            response.setTotalSize(totalSize);
            responsesList.add(response);
        }
        Collections.sort(responsesList, (o1, o2) -> o1.getDate().compareTo(o2.getDate()));

        return responsesList;

    }

    /**
     * 获取文件地址
     * @param request
     * @return
     */
    @SneakyThrows
    @Override
    public GetFileUrlResponse getFileUrl(GetFileUrlRequest request){
        GetFileUrlResponse response = new GetFileUrlResponse();

        String cacheKey = RedisConstant.STRAIGHT_DOWNLOAD_URL + request.getUserDTO().getId() + ":" +  request.getFileId();
        String url = stringRedisTemplate.opsForValue().get(cacheKey);
        // Redis存在 则从Redis获取
        if (StringUtils.isNotBlank(url)){
            response.setUrl(url);
            return response;
        }else{
            // minio生成
            // 从数据库中查询是否存在
            GetUserFileRequest getUserFileRequest = new GetUserFileRequest();
            getUserFileRequest.setId(request.getFileId());
            getUserFileRequest.setUserId(request.getUserDTO().getId());
            getUserFileRequest.setUploadStatus(EUploadStatus.DONE_UPLOAD.getStatus());
            getUserFileRequest.setIsDelete(EDeleteStatus.UN_DELETE.getStatus());
            List<File> fileList  =  fileService.getFileList(getUserFileRequest);
            if (CollectionUtils.isNotEmpty(fileList)){
                File file = fileList.get(0);
                if (null == file){
                    throw new BusinessException("文件不存在");
                }                    Map<String,String> reqParams = new HashMap<>();
                ContentInfo contentInfo = ContentInfoUtil.findExtensionMatch(file.getObjectName());
                if (null != contentInfo){
                    reqParams.put("response-content-type",contentInfo.getMimeType());
                }
                url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(file.getBucket()).object(file.getObjectName()).method(Method.GET).build());
                stringRedisTemplate.opsForValue().set(cacheKey,url);
                response.setUrl(url);
                return response;
            }else{
                throw new BusinessException("文件不存在");
            }

        }
    }

    /**
     * 修改文件名字
     * @param request
     */
    @Override
    public void updateFileName(UpdateFileNameRequest request) {
        if (StringUtils.isNotBlank(request.getFileName())) {
            File file = new File();
            file.setUpdateBy(request.getUserDTO().getUsername());
            file.setUpdateTime(new Date());
            file.setFileName(request.getFileName());
            file.setId(request.getId());
            fileService.updateFileName(file);
        }
    }

    /**
     * 移动文件
     * @param request
     */
    @Override
    public void moveFile(MoveFileRequest request){
        List<String> moveFileIdList = request.getMoveFileIdList();
        if (CollectionUtil.isNotEmpty(moveFileIdList)){

            BatchMoveFileRequest batchMoveFileRequest = new BatchMoveFileRequest();
            batchMoveFileRequest.setDirectoryId(Long.valueOf(request.getMoveInDirectoryId()));
            batchMoveFileRequest.setFileIdList(moveFileIdList);
            batchMoveFileRequest.setUpdateBy(request.getUserDTO().getUsername());
            batchMoveFileRequest.setUpdateTime(new Date());

            fileService.batchMoveFile(batchMoveFileRequest);
        }else {
            throw new BusinessException("文件列表为空，不可移动");
        }
    }

    /**
     * 上传图片
     * @param file
     * @return
     */
    @SneakyThrows
    @Override
    public UploadImgResponse uploadImg(MultipartFile file){

        UploadImgResponse response = new UploadImgResponse();
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(imgBucketName).build());
        if (!flag){
            // 如果bucket不存在 则创建
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(imgBucketName).build());
            String policyJson = "{\n" +
                    "  \"Statement\": [\n" +
                    "        {\n" +
                    "            \"Action\": [\n" +
                    "                \"s3:GetBucketLocation\",\n" +
                    "                \"s3:ListBucket\"\n" +
                    "            ],\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Principal\": \"*\",\n" +
                    "            \"Resource\": \"arn:aws:s3:::" + imgBucketName + "\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"Action\": \"s3:GetObject\",\n" +
                    "            \"Effect\": \"Allow\",\n" +
                    "            \"Principal\": \"*\",\n" +
                    "            \"Resource\": \"arn:aws:s3:::" + imgBucketName + "/*\"\n" +
                    "        }\n" +
                    "    ],\n" +
                    "    \"Version\": \"2020-12-25\"\n" +
                    "}";
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(imgBucketName).config(policyJson).build());
        }
        String fileName = IdWorker.getIdStr();
        String contentType = null;
        Map<String,String> headerMap = new HashMap<>();
        ContentInfo contentInfo = ContentInfoUtil.findExtensionMatch(file.getOriginalFilename());
        if (null != contentInfo){
            contentType = contentInfo.getMimeType();
            headerMap.put("response-content-type",contentInfo.getMimeType());
        }
        InputStream inputStream = file.getInputStream();
        int available = inputStream.available();
        minioClient.putObject(PutObjectArgs.builder().bucket(imgBucketName).object(fileName).contentType(contentType).stream(inputStream,available,-1).build());

        String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(imgBucketName).object(fileName).method(Method.GET).extraQueryParams(headerMap).build());

        response.setImgUrl(url);
        return response;
    }
}
